package com.bph.schedule.algorithm;

import com.bph.commons.pojo.User;
import com.bph.commons.utils.SP;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 统一定义一些数据存储类型以及数据处理方法
 */
public class Data {

    /**
     * 变量使用规定
     * 遍历集合数据：i,j,k,m,n,p,q
     * 集合数据长度：len
     * 标志变量：flag  xxFlag
     * 计数变量：count xxCount
     * 中转变量：temp tmp  xxTemp
     */

    /**
     * 用户偏好 {"pStage":"8,12;16,20","pTime":"3,4","pSex":1}
     * 注意：它不同于pojo中的User_Preference
     */
    public static LinkedHashMap<String,Object> UserPreference;


    /**
     * 每日工作情况{"daySchedule":"8,10;14,16","weekTime":10,
     * "had_lunch":0,"had_supper":0,"had_break":0}
     */
    public static LinkedHashMap<String,Object> DayStatus;


    /**
     * 用户数据 [{"user":User,"pre":UserPreference,"day":DayStatus}]
     * [{"user_id":1,"user_name":"张三","user_sex":"男","user_title":"店员","pStage":"8,12;16,20","pTime":"3,4","pSex":1,...}]
     * 注意：允许新增偏好，但建议从系统给定的偏好中选择，新增不存在的偏好需要修改代码，这可以放在系统升级时通过修改代码去做
     */
    public static ArrayList<LinkedHashMap<String,Object>> EmployeeInfo;


    /**
     * 客流数据信息{"时间":"预测客流"}
     */
    public static LinkedHashMap<String,String> Flow;


    /**
     * 日期信息,最大31天会有868个半小时
     */
    public static String[] Date = new String[868];


    /**
     * 一周的的实际班次需求表 [{"8,10.5":2,"9.5,12":3,"14,16":1,...}{...}]
     * 如上表示第一天的8点到10点半需要时刻有两个人在岗位上（可能中间会有其他人来替班，但一定要保持两个人）
     * 注意：String键表示班次起止时间
     */
    public static ArrayList<LinkedHashMap<String, Integer>> EmployeeNeeds;


    /**
     * 最终排班表{"8,8.5":[{1,"张三","店员"},{2,"李四","店长"}],"8.5,9":[{},{}],...}
     */
    public static LinkedHashMap<String, ArrayList<User>> Schedule;


    //上月遗留周时长
    public static LinkedHashMap<String,Double> lastMonth = new LinkedHashMap<>();


    //门店面积
    public static double area = 100;

    //固定规则
    //门店营业时间(工作日，周末)
    public static  String[][] shopTime = {{"8","22"},{"8,24"}};
    //工作时长规则
    public static double weekMax = 40; //每周最长工作时间
    public static double dayMax = 8; //每日最长工作时间
    public static double minHour = 2; //单个班次最短时间
    public static double maxHour = 4; //单个班次最长时间
    public static boolean workSeries = true; //员工是否可连续排多个班次
    public static double workMax = 4; //员工连续工作最大时长
    public static boolean mustMeal = true; //是否必须给工作时间连续覆盖就餐时间的员工安排就餐时间
    public static double relax = 0.5; //默认休息时长
    public static double mealTime = 0.5; //默认就餐时长
    //休息时间段
    public static String[][] lunchTime = {{"11","14","0.5"}};
    public static String[][] supperTime = {{"17","20","0.5"}};
    public static String[][] relaxTime = {{"8","22","0.5"}};

    //用户自定义规则
    //开始营业前排班
    public static double preHour = 1; //开店前准备工作时长
    public static double preAdjust = 100; //门店面积/店员需求量 系数
    public static int preNum =(int)Math.ceil(Data.area/Data.preAdjust); //开店前需要的员工个数
    public static String[] preTitle = {"清洁","保安"}; //可选岗位，每种岗位至少有1人
    public static LinkedHashMap<String,Integer> preMap = new LinkedHashMap<>();
    //正常营业时排班
    public static double needFactor = 3.8;//店员需求系数
    public static String[] title = {"销售","收银","前台","保安","清洁"}; //每种职位至少有1人
    //结束营业后排班
    public static double postHour = 2; //关店后工作时长
    public static double postAdjust1 = 80; //门店面积/店员需求量 系数
    public static double postAdjust2 = 80; //门店面积/店员需求量 系数
    public int postNum =(int)Math.ceil(Data.area/Data.postAdjust1+Data.postAdjust2); //关店后需要的员工个数
    public String[] postTitle = {"清洁","保安","收银"}; //可选岗位，每种岗位至少1人
    public static LinkedHashMap<String,Integer> postMap = new LinkedHashMap<>();
    //客流小于1时排班
    public static int minNeed = 1; //至少1人




    /**
     * 根据客流数据计算店员需求量，
     * @param customerFlow 预测客流量
     * @return
     */
    public static ArrayList<LinkedHashMap<String, Integer>> scheduleRequired(
            List<LinkedHashMap<String, Object>> customerFlow){

        //用于接收最终结果
        ArrayList<LinkedHashMap<String, Integer>> scheduleRequired = new ArrayList<LinkedHashMap<String,Integer>>();
        //用于存放店员需求量数据
        int[] need = new int[customerFlow.size()];
        //存放客流的相关数据信息
        LinkedHashMap<String,String> flow = new LinkedHashMap<>();
        //根据客流计算每0.5h的店员需求量数据：预测客流/3.8 = 店员需求数。3.8为缺省值
        Iterator<LinkedHashMap<String, Object>> iterator = customerFlow.iterator();
        int i=0;
        while (iterator.hasNext()) {
            LinkedHashMap<String,Object> flowMap = iterator.next();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String startTime = sdf.format(flowMap.get("message_startTime"));
            String finishTime = sdf.format(flowMap.get("message_finishTime"));
            Date[i] = startTime+","+finishTime;
            //客流为NULl先默认为0，至少需要一人值班
            if(flowMap.get("customerFlow") == null){
                need[i] = 0;
            } else{
                need[i] = (int) Math.ceil(Double.parseDouble(String.valueOf(flowMap.get("customerFlow"))) / Data.needFactor);
            }
            //客流为0时最少一个店员值班
            if (need[i] == 0)
                need[i] = Data.minNeed;
            Flow = new LinkedHashMap<>();
            Flow.put(String.valueOf(flowMap.get("message_startTime")),String.valueOf(need[i]));
            i++;
        }

        //调用Lp逐天进行班次规划，保证总体员工时间最短
        int count = 0; //记录完成了几个0.5h时间段的排班
        int day = 0; //记录完成了几天的排班
        //先从每周店员需求表中拿出一天的数据进行班次规划
        int[] dayNeed = new int[28];
        while(count<need.length){
            LinkedHashMap<String,Integer> temp = new LinkedHashMap<>();
            for(int j=0;j<dayNeed.length;j++){
                dayNeed[j] = need[count+j];
            }

            //Lp.adjust返回的是[[2,2.5,2],[...]]
            //表示从第一个0.5h（8:00）开始的班次有两个2h班次，一个2.5h班次
            //todo 期望经过处理，Lp返回的是{"开始时间,结束时间",班次个数}
            double[][] daySchedule = Lp.adjust(dayNeed);
//            System.out.println("第"+(day+1)+"天的Lp规划结果为：");
//            for(int m=0;m<daySchedule.length;m++){
//                System.out.println("第"+(m+1)+"个0.5h开始：");
//                for(int n=0;n<daySchedule[m].length;n++)
//                {
//                    System.out.print(daySchedule[m][n]+"__");
//                }
//                System.out.println("\n");
//            }

            //4小时班次实际由两个2小时班次组成，为避免4小时班次将可工作2小时的人pass掉，这里将4小时班次统一分成2个2小时班次
            for(int p=0;p+4<daySchedule.length;p++){
                for(int q=0;q<daySchedule[p].length;q++){
                    if(daySchedule[p][q] == 4.0){
                        daySchedule[p][q] = 2.0;
                        int len = daySchedule[p+4].length;
                        double[] tmp = new double[len+1];
                        for(int k=0;k<len;k++){
                            tmp[k] = daySchedule[p+4][k];
                        }
                        tmp[len] = 2.0; //把分出的2小时放到2h后的那个0.5班次
                        daySchedule[p+4] = tmp.clone();
                    }
                }
            }
//            System.out.println("拆分4小时班次后的规划结果为：");
//            for(int m=0;m<daySchedule.length;m++){
//                for(int n=0;n<daySchedule[m].length;n++)
//                {
//                    System.out.print(daySchedule[m][n]+"__");
//                }
//                System.out.println("\n");
//            }

            //todo Lp的缺陷，返回值形式与ScheduleHandler排班需要的形式不同，所以要做如下转换
            //将{{2,2.5，3},{2.5,4,2}}转化为--->{{"开始，结束",个数}{...}}
            for(int m=0;m<daySchedule.length;m++){//第几个0.5班次
                for(int n=0;n<daySchedule[m].length;n++){//0.5班次中的第几个需求班次
                    if(daySchedule[m][n] != 0.0){
                        String start = String.valueOf(8+m*0.5);
                        String end = String.valueOf(8+m*0.5+daySchedule[m][n]);
                        String stage = start+","+end;
                        if(temp.containsKey(stage)){
                            int times = temp.get(stage);
                            temp.put(stage,times+1);
                        }else{
                            temp.put(stage,1);
                        }
                    }
                }
            }
//            System.out.println("本日班次需求如下：");
//            for(String key:temp.keySet()){
//                System.out.println(key+":"+temp.get(key));
//            }

            scheduleRequired.add(day,temp); //把一天的班次需求数据放到一周的总表中
            count += 28;
            day += 1;
        }
        return scheduleRequired;
    }


    /**
     * 获取员工数据信息
     * @param user 用户基本信息
     * @param upre 用户偏好信息
     * @return
     */
    public static LinkedHashMap<String,Object> employeeInfo(User user,LinkedHashMap<String,Object> upre){
        LinkedHashMap<String,Object> info = new LinkedHashMap<>();
        info.put("user_id",user.getUser_id()); //员工编号
        info.put("user_name",user.getUser_name()); //员工姓名
        info.put("user_sex",user.getUser_name());//员工性别
        info.put("user_title",user.getUser_title()); //员工职位
        info.put("pStage",upre.get("pStage"));//员工偏好班次
        info.put("pTime",upre.get("pTime"));//员工偏好时长
        info.put("pSex",upre.get("pSex"));//员工偏好伙伴性别
        info.put("dSchedule","0,0"); //员工日排班情况
        info.put("had_lunch","0"); //员工是否吃过午饭，1是0否
        info.put("had_supper","0");//员工是否吃过晚饭,1是0否
        info.put("had_break","0");//当前班次之前连上四小时的员工是否已休息
        info.put("weekTime","0");//员工周累计工作时间
        return info;
    }

    /**
     * 按weekTime给employeeInfo排序
     */
    public static ArrayList<LinkedHashMap<String,Object>> sortByWeekTime(
            ArrayList<LinkedHashMap<String,Object>> employeeInfo){
                employeeInfo.sort(new Comparator<LinkedHashMap<String, Object>>() {
                    @Override
                    public int compare(LinkedHashMap<String, Object> o1, LinkedHashMap<String, Object> o2) {
                        double m = Double.parseDouble(String.valueOf(o1.get("weekTime")));
                        double n = Double.parseDouble(String.valueOf(o2.get("weekTime")));
                        int sign = -1;
                        if (m - n > 0)
                            sign = 0;
                        return sign;
                    }
                });
        return employeeInfo;
    }


    /**
     * 对LinkedHashMap按key进行降序排序
     * @param map
     * @return
     */
    public static LinkedHashMap<String,ArrayList<User>> sortByKey(LinkedHashMap<String, ArrayList<User>> map) {
        List<Map.Entry<String, ArrayList<User>>> infoIds =new ArrayList<Map.Entry<String,ArrayList<User>>>(map.entrySet());
        //排序
        Collections.sort(infoIds, new Comparator<Map.Entry<String, ArrayList<User>>>() {
            public int compare(Map.Entry<String, ArrayList<User>> o1, Map.Entry<String, ArrayList<User>> o2) {
                double p1 = Double.parseDouble(SP.str_Parse(o1.getKey())[0]);
                double p2 = Double.parseDouble(SP.str_Parse(o2.getKey())[0]);
                int flag = -1;
                if(p1-p2>0)
                    flag = 0;
                return flag; //升序
            }
        });
        //转换成新map输出
        LinkedHashMap<String, ArrayList<User>> newMap = new LinkedHashMap <String, ArrayList<User>>();
        for(Map.Entry<String,ArrayList<User>> entity : infoIds){
            newMap.put(entity.getKey(), entity.getValue());
        }
        return newMap;
    }



//    //测试代码
//    public static void main(String[] args) {
//        ArrayList<LinkedHashMap<String,Object>> a = new ArrayList<>();
//        LinkedHashMap<String,Object> l1 = new LinkedHashMap<>();
//        LinkedHashMap<String,Object> l2 = new LinkedHashMap<>();
//        l1.put("name","张三");
//        l1.put("weekTime",2.5);
//        l2.put("name","李四");
//        l2.put("weekTime",1);
//        a.add(l1);
//        a.add(l2);
//        a = Data.sortByWeekTime(a);
//
//        for(LinkedHashMap<String,Object> l:a){
//            for(String key:l.keySet()){
//                System.out.println(key+"/"+l.get(key));
//            }
//        }
//    }
}
